按照传统的经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用 synchronized 进行线程同步,但线程同步机制会降低并发性,影响系统性能。ThreaLocal 可以改变这种方式。
认识
JDK 1.2 版本开始提供 java.lang.ThreaLocal,ThreaLocal 为解决多线程程序的并发问题提供了新的思路。ThreaLocal 是线程的一个本地化对象,当工作于多线程中的对象使用 ThreadLocal 维护变量时,它会为每个使用该变量的线程分配一个独立的变量副本。所有每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本;从线程的角度看,这个变量就像是线程的本地变量。
接口方法
ThreadLocal 支持泛型,且有四个简单的接口:
- void set(T value):设置当前线程的线程局部变量的值。
- public T get():该方法返回当前线程所对应的线程局部变量。
- public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用(JDK 5.0 新增)。当线程结束后,对应该线程的局部变量将会自动被垃圾回收,所以显示调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected T initialValue():返回该线程局部变量的初始值,该方法是一个 protected 方法,为了让子类覆盖而设计。
ThreadLocal 的实现思路:在 ThreadLocal 内部有一个 Map,用于存储每一个线程的变量副本,Map 中的元素的 key 为线程对象,value 为对应线程的变量副本。
实例
|
|
运行结果:
从输出结果上看,由于 ThreadLocal 为每一个线程提供了单独的副本,所以每个线程所产生的序号虽然都共享同一个 SequenceNumber 实例,但它们没有发生相互干扰的情况,而是各自产生独立的序列号。
与线程同步机制的比较
在线程同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序解决什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等复杂的问题。
ThreadLocal 从另一个角度解决多线程的并发访问,为每个线程提供独立的变量副本,从而隔离了多个线程对访问数据的冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对变量进行同步。ThreadLocal 提供了线程安全的对象封装,在编写多线程代码时,可以把部安全的变量封装进 ThreadLocal。
总的来说,同步机制采用“以时间换空间”的方式:访问串行化,对象共享化;而 ThreadLocal 采用“以空间换时间”的方式:访问并行化,对象独享化。